package frame.service.standard.impl;

import frame.service.anno.MiayAction;
import frame.service.anno.MiayActionParam;
import frame.service.anno.MiayService;
import frame.service.context.ServiceContext;
import frame.service.standard.Constants;
import frame.service.standard.MiayFilter;
import frame.service.standard.MiayRuntimeException;
import frame.service.standard.ServiceCheck;
import frame.service.util.FrameUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

public class LocalServiceExecutor extends AbstractServiceExecutor implements ApplicationContextAware, InitializingBean {
    private Logger logger = LoggerFactory.getLogger(LocalServiceExecutor.class);
    private ApplicationContext applicationContext;
    protected Map<String, ServiceInfo> servicesMap = new HashMap<>();

    @Override
    public void innerExec(ServiceInfo serviceInfo, ServiceContext context) throws Exception {
        String action = context.getReqContentString(Constants.REQUEST_ACTION);
        String version = context.getReqContentString(Constants.REQUEST_VERSION);

        Method method = serviceInfo.getSupportAction().get(FrameUtil.generateActionId(action, version));
        logger.info("Request Action:{}", FrameUtil.generateActionId(action, version));
        if(method==null){
            logger.error("920002");
            throw new MiayRuntimeException("920002");
        }else{
            boolean checkPass = true;
            String checkErrorName = null;

            //服务自定义检查器
            if(serviceInfo.getChecks()!=null && serviceInfo.getChecks().size()!=0){
                Iterator<ServiceCheck> iterator = serviceInfo.getChecks().iterator();
                while (iterator.hasNext()){
                    ServiceCheck next = iterator.next();
                    if(!next.check(context)){
                        checkPass = false;
                        checkErrorName = next.toString();
                        break;
                    }
                }
            }

            if(checkPass){
                Parameter[] parameters = method.getParameters();
                List<Object> serviceParams = new ArrayList<>();
                for(Parameter param: parameters){
                    if(param.getParameterizedType().getTypeName().equals(ServiceContext.class.getName())){
                        //如果是serviceContext
                        serviceParams.add(context);
                        continue;
                    }else{
                        //默认将转换为request bean
                        Class<?> paramType = param.getType();
                        Object o = paramType.newInstance();
                        BeanInfo beanInfo = Introspector.getBeanInfo(paramType);
                        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

                            Method writeMethod = propertyDescriptor.getWriteMethod();
                            if(writeMethod==null){
                                continue;
                            }
                            MiayActionParam annotation = writeMethod.getAnnotation(MiayActionParam.class);
                            String key = null;
                            if(annotation!=null){
                                key = "".equals(annotation.value())?propertyDescriptor.getName():annotation.value();
                            }else{
                                continue;
                            }
                            writeMethod.invoke(o, context.getReqContent(key));
                        }
                        serviceParams.add(o);
                    }
                }

                Object returnRes = method.invoke(serviceInfo.getObject(), serviceParams.toArray());
                //判断返回值
                if(returnRes!=null){
                    if(returnRes.getClass().isPrimitive()){
                        //如果返回值为基础性
                        context.getRespData().put(Constants.RESPONSE_DEFAULT_BODY_KEY, returnRes);
                    }else{
                        BeanInfo beanInfo = Introspector.getBeanInfo(returnRes.getClass());
                        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                            Method writeMethod = propertyDescriptor.getWriteMethod();
                            if(writeMethod==null){
                                continue;
                            }
                            MiayActionParam annotation = writeMethod.getAnnotation(MiayActionParam.class);
                            if(annotation!=null){
                                String key = "".equals(annotation.value())?propertyDescriptor.getName():annotation.value();
                                Object val = propertyDescriptor.getReadMethod().invoke(returnRes);
                                context.getRespData().put(key, val);
                            }
                        }
                    }
                }
            }else{
                logger.error("920005");
                throw new MiayRuntimeException("920005", new Object[]{checkErrorName});
            }
        }
    }

    @Override
    public ServiceInfo getServiceInfo(ServiceContext context) {
        String safeMode = context.getReqContentString(Constants.REQUEST_SAFE_MODE);
        String module = context.getReqContentString(Constants.REQUEST_MODULE);
        String service = context.getReqContentString(Constants.REQUEST_SERVICE);

        String serviceId = FrameUtil.generateServiceId(safeMode,module, service);
        logger.info("Request Service:{}", serviceId);
        return servicesMap.get(serviceId);
    }

    /**
     * 1.搜索applicationContext内的被MiayService注解的类
     * 2.搜索MiayService类内MiayAction注解方法
     * 3.完成服务地址簿注册
     * */
    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(MiayService.class);
        Iterator<Map.Entry<String, Object>> iterator = beansWithAnnotation.entrySet().iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next().getValue();
            ServiceInfo serviceInfo = createServiceInfo(obj);
            if(servicesMap.get(serviceInfo.getServiceId())!=null){
                throw new MiayRuntimeException("920003", new Object[]{serviceInfo.getServiceId()});
            }
            servicesMap.put(serviceInfo.getServiceId(), serviceInfo);
        }
        if(filters!=null){
            Collections.sort(filters, new Comparator<MiayFilter>() {
                @Override
                public int compare(MiayFilter f1, MiayFilter f2) {
                    return f1.getOrder()>f2.getOrder()?1:-1;
                }
            });
        }
    }

    /**
     * 服务配置解析
     * */
    public ServiceInfo createServiceInfo(Object object) throws Exception{
        ServiceInfo ret = new ServiceInfo();
        ret.setObject(object);
        MiayService annotation = object.getClass().getAnnotation(MiayService.class);
        ret.setServiceId(FrameUtil.generateServiceId(annotation.safeMode(), annotation.module(), annotation.service()));
        Map<String, Method> methods = new HashMap<>();
        String[] checkers = annotation.checkers();
        if(checkers!=null && checkers.length!=0){
            List<ServiceCheck> checkList = new ArrayList<>(checkers.length);
            for(String checkName:checkers){
                ServiceCheck bean = applicationContext.getBean(checkName, ServiceCheck.class);
                checkList.add(bean);
            }
            ret.setChecks(checkList);
        }

        Method[] declaredMethods = object.getClass().getDeclaredMethods();
        for(Method method: declaredMethods){
            MiayAction actionAnno = method.getAnnotation(MiayAction.class);
            if(actionAnno!=null){
                String actionRegName = method.getName();
                if(!"".equals(actionAnno.name())){
                    actionRegName = actionAnno.name();
                }
                String actionName = FrameUtil.generateActionId(actionRegName, actionAnno.version());
                if(methods.get(actionName)!=null){
                    throw new MiayRuntimeException("920004", new Object[]{ret.getServiceId(), actionName});
                }
                methods.put(actionName, method);
            }
        }
        Set<String> serviceSupportHttpMethod = new HashSet<>();
        for(String method:annotation.supportHttpMethod()){
            serviceSupportHttpMethod.add(method.toLowerCase());
        }
        ret.setSupportHttpMethod(serviceSupportHttpMethod);
        ret.setSupportAction(methods);
        return ret;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
